﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace G.Controls
{
    public enum Dock
    {
        Left,
        Top,
        Right,
        Bottom,
        Non
    }

    public class DockPanel : Panel
    {
        private static bool _ignorePropertyChange;
        public static readonly DependencyProperty DockProperty = DependencyProperty.RegisterAttached("Dock", typeof(Dock), typeof(DockPanel), new PropertyMetadata(Dock.Left, new PropertyChangedCallback(DockPanel.OnDockPropertyChanged)));
        public static readonly DependencyProperty LastChildFillProperty = DependencyProperty.Register("LastChildFill", typeof(bool), typeof(DockPanel), new PropertyMetadata(true, new PropertyChangedCallback(DockPanel.OnLastChildFillPropertyChanged)));

        protected override Size ArrangeOverride(Size arrangeSize)
        {
            double left = 0.0;
            double top = 0.0;
            double right = 0.0;
            double bottom = 0.0;
            UIElementCollection children = base.Children;
            int dockedCount = children.Count - (this.LastChildFill ? 1 : 0);
            int index = 0;
            foreach (UIElement element in children)
            {
                Rect remainingRect = new Rect(left, top, Math.Max((double)0.0, (double)((arrangeSize.Width - left) - right)), Math.Max((double)0.0, (double)((arrangeSize.Height - top) - bottom)));
                if (index < dockedCount)
                {
                    Size desiredSize = element.DesiredSize;
                    switch (GetDock(element))
                    {
                        case Dock.Left:
                            left += desiredSize.Width;
                            remainingRect.Width = desiredSize.Width;
                            break;

                        case Dock.Top:
                            top += desiredSize.Height;
                            remainingRect.Height = desiredSize.Height;
                            break;

                        case Dock.Right:
                            right += desiredSize.Width;
                            remainingRect.X = Math.Max((double)0.0, (double)(arrangeSize.Width - right));
                            remainingRect.Width = desiredSize.Width;
                            break;

                        case Dock.Bottom:
                            bottom += desiredSize.Height;
                            remainingRect.Y = Math.Max((double)0.0, (double)(arrangeSize.Height - bottom));
                            remainingRect.Height = desiredSize.Height;
                            break;
                    }
                }
                element.Arrange(remainingRect);
                index++;
            }
            return arrangeSize;
        }

        public static Dock GetDock(UIElement element)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }
            return (Dock)element.GetValue(DockProperty);
        }

        protected override Size MeasureOverride(Size constraint)
        {
            double usedWidth = 0.0;
            double usedHeight = 0.0;
            double maximumWidth = 0.0;
            double maximumHeight = 0.0;
            foreach (UIElement element in base.Children)
            {
                Size remainingSize = new Size(Math.Max((double)0.0, (double)(constraint.Width - usedWidth)), Math.Max((double)0.0, (double)(constraint.Height - usedHeight)));
                element.Measure(remainingSize);
                Size desiredSize = element.DesiredSize;
                switch (GetDock(element))
                {
                    case Dock.Left:
                    case Dock.Right:
                        {
                            maximumHeight = Math.Max(maximumHeight, usedHeight + desiredSize.Height);
                            usedWidth += desiredSize.Width;
                            continue;
                        }
                    case Dock.Top:
                    case Dock.Bottom:
                        {
                            maximumWidth = Math.Max(maximumWidth, usedWidth + desiredSize.Width);
                            usedHeight += desiredSize.Height;
                            continue;
                        }
                }
            }
            maximumWidth = Math.Max(maximumWidth, usedWidth);
            return new Size(maximumWidth, Math.Max(maximumHeight, usedHeight));
        }

        private static void OnDockPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (_ignorePropertyChange)
            {
                _ignorePropertyChange = false;
            }
            else
            {
                UIElement element = (UIElement)d;
                Dock value = (Dock)e.NewValue;
                if (((value != Dock.Left) && (value != Dock.Top)) && ((value != Dock.Right) && (value != Dock.Bottom)))
                {
                    _ignorePropertyChange = true;
                    element.SetValue(DockProperty, (Dock)e.OldValue);
                    throw new ArgumentException("DockPanel_OnDockPropertyChanged_InvalidValue");
                }
                DockPanel panel = VisualTreeHelper.GetParent(element) as DockPanel;
                if (panel != null)
                {
                    panel.InvalidateMeasure();
                }
            }
        }

        private static void OnLastChildFillPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as DockPanel).InvalidateArrange();
        }

        public static void SetDock(UIElement element, Dock dock)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }
            element.SetValue(DockProperty, dock);
        }

        public bool LastChildFill
        {
            get
            {
                return (bool)base.GetValue(LastChildFillProperty);
            }
            set
            {
                base.SetValue(LastChildFillProperty, value);
            }
        }
    }
}
